package com.springmvc.web.servlet;

import com.springmvc.web.constant.Const;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import com.springmvc.web.context.WebApplicationContext;

import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.Locale;


/**
 * 这个对象是SpringMVC的核心，所有的请求都会经过这个对象
 * 这个对象是tomcat new出来，然后调用方法
 * 继承了 HttpServlet  根据生命周期 new出来后 init方法调用，然后service方法调用
 */
public class DispatcherServlet extends HttpServlet {
    /**
     * 视图解析器
     */
    private ViewResolver viewResolver;
    /**
     * 处理器映射器
     */
    private HandlerMapping handlerMapping;

    /**
     * 处理器适配器
     */
    private HandlerAdapter handlerAdapter;


    @Override
    public void init() throws ServletException {
        // 初始化IOC 容器
        // 1. 读取配置文件
        // getServletConfig是tomcat（web容器）实现的
        ServletConfig servletConfig = getServletConfig();
        // web.xml中配置的contextConfigLocation
        String contextConfigLocation = servletConfig.getInitParameter(Const.CONTEXT_CONFIG_LOCATION);
        System.out.println("springmvc配置文件---->" + contextConfigLocation);
        // 如果 配置文件是classpath:开头，则从类路径下读取配置文件
        String springMvcConfigPath = null;
        if (contextConfigLocation.trim().startsWith(Const.PREFIX_CLASSPATH)) {
            // 读取配置文件
            // substring ==> 去掉classpath:
            String substring = contextConfigLocation.substring(Const.PREFIX_CLASSPATH.length());
            // /D:/java_workspace/my/myspringmvc/target/myspringmvc-1.0-SNAPSHOT/WEB-INF/classes/springmvc.xml
            springMvcConfigPath = Thread.currentThread().getContextClassLoader().getResource(substring).getPath();
            // 对路径中特殊字符处理  防止解析出 % &。。。。
            springMvcConfigPath = URLDecoder.decode(springMvcConfigPath, Charset.defaultCharset());
            System.out.println("springmvc配置文件的绝对路径---->" + springMvcConfigPath);
        }
        // 初始化spring web容器  就是 WebApplicationContext   他是 ApplicationContext 的子类
        // webApplicationContext 代表的是 springweb容器， 最好存储到Servlet上下文中
        WebApplicationContext webApplicationContext = new WebApplicationContext(this.getServletContext(), springMvcConfigPath);
        this.getServletContext().setAttribute("webApplicationContext", webApplicationContext);

        // 初始化处理器映射器
        this.handlerMapping = (HandlerMapping) webApplicationContext.getBean(Const.HANDLER_MAPPING);
        // 初始化处理器适配器
        this.handlerAdapter = (HandlerAdapter) webApplicationContext.getBean(Const.HANDLER_ADAPTER);
        // 初始化视图解析器
        this.viewResolver = (ViewResolver) webApplicationContext.getBean(Const.VIEW_RESOLVER);
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doDispatch(req, resp);
    }

    /**
     * 核心方法，处理请求
     *
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            // 1. 根据请求获取处理器执行链                 handlerMapping:处理器映射器   根据请求路径和请求方式映射到具体的处理器方法
            HandlerExecutionChain mappedHandler = handlerMapping.getHandler(request);
            System.out.println(mappedHandler);
            // 2. 根据处理器方法获取适合的处理器适配器  这里只考虑一个
            //  源程序： HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());  会去循环集合
            HandlerAdapter ha = this.handlerAdapter;
            // 3. 执行拦截器的preHandle方法
            if (!mappedHandler.applyPreHandle(request, response)) {
                return;
            }
            // 4. 执行处理器,返回ModelAndView
            // Actually invoke the handler.
            ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler());
            // 5. 执行拦截器的postHandle方法
            mappedHandler.applyPostHandle(request, response, mv);
            // 6. 响应
            // 6.1 获取视图解析器
            View view = viewResolver.resolveViewName(mv.getView().toString(), Locale.CHINA);
            // 6.2 执行视图解析器 就是渲染
            view.render(mv.getModel(), request, response);
            // 7. 执行拦截器的afterCompletion方法
            mappedHandler.triggerAfterCompletion(request, response,  null);
        } catch (Exception e) {

        }

    }

}



